﻿using FM.Core.Storage;
using FM.Storage.Abstract;
using FM.Storage.Models;
using FM.Util;
using FM.Util.Helper;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace FM.Storage.Concrete.LocalProvider
{
    public class LocalFileManager : ILocalFileManager
    {
        /// <summary>
        /// 分片临时根目录
        /// </summary>
        private string temporaryFolder = "temp";
        private UploadServerConfig _config;

        private string App = "Default";


        public async Task<UploadResult> ChunkUploadAsync(HttpRequest request)
        {
            try
            {
                //获取上传的文件分片
                var file = request.Form.Files.FirstOrDefault();
                if (file == null || file.Length == 0)
                {
                    return new UploadResult()
                    {
                        msg = "没有文件数据"
                    };
                }

                //获取参数                    
                GetParams(request, out var chunkNumber, out var chunkSize, out var totalSize, out string identifier,
                    out string filename, out string relativePath, out int totalChunks);

                //大小验证
                if (totalSize > _config.GetByteSize())
                {
                    return new UploadResult()
                    {
                        msg = "文件太大"
                    };
                }

                //后缀验证
                var ext = Path.GetExtension(filename);
                if (!_config.AllowExts.Contains(ext, StringComparison.OrdinalIgnoreCase))
                {
                    return new UploadResult()
                    {
                        msg = "不允许文件扩展名"
                    };
                }
                var validMsg = ValidateRequest(chunkNumber, chunkSize, totalSize, identifier, filename, null,
                        totalChunks, _config.GetByteSize());
                if (validMsg != null)
                {
                    return new UploadResult()
                    {
                        msg = validMsg
                    };
                }
                else
                {
                    var chunkFilename = GetChunkFilename(_config.PhysicalPath, chunkNumber, identifier);
                    using (var fileStream = System.IO.File.OpenWrite(chunkFilename))
                    {
                        var stream = file.OpenReadStream();
                        stream.CopyTo(fileStream);
                        fileStream.Flush(true);
                        countDict.AddOrUpdate(identifier, 1, (key, oldValue) => oldValue + 1);
                    }

                    if (chunkNumber == totalChunks)
                    {
                        //验证块的完整性
                        while (true)
                        {
                            if (countDict.GetValueOrDefault(identifier) < totalChunks)
                            {
                                await Task.Delay(TimeSpan.FromMilliseconds(500));
                            }
                            else
                            {
                                countDict.Remove(identifier, out _);
                                break;
                            }
                        }
                        //merge file;
                        string[] chunkFiles = Directory.GetFiles(
                            Path.Combine(_config.PhysicalPath, temporaryFolder),
                            "uploader-" + identifier + ".*",
                            SearchOption.TopDirectoryOnly);
                        var fileUrl = await MergeChunkFiles("default", ext, chunkFiles);
                        return new UploadResult()
                        {
                            ok = true,
                            url = fileUrl
                        };
                    }
                    else
                    {
                        return new UploadResult()
                        {
                            ok = true,
                            msg = "部分上传完成"
                        };
                    }
                }
            }
            catch (Exception exp)
            {
                LogUtil.Error(exp.InnerException.Source);
                return new UploadResult()
                {
                    msg = exp.Message
                   
                };
            }
        }

        public async Task<UploadResult> UploadAsyncAsync(HttpRequest request)
        {

          
            try
            {
                //获取上传的文件
                var file = request.Form.Files.FirstOrDefault();
                if (file == null || file.Length == 0)
                {
                    return new UploadResult() { msg = "没有文件数据" };
                }

                //大小验证
                if (file.Length > _config.GetByteSize())
                {
                    return new UploadResult() { msg = "文件大小超过限制" };
                }

                //后缀验证
                var ext = Path.GetExtension(file.FileName);
                if (!(_config.AllowExts).Contains(ext, StringComparison.OrdinalIgnoreCase))
                {
                    return new UploadResult() { msg = "不允许上传的文件扩展名" };
                }

                //上传逻辑
                var now = DateTime.Now;
                var yy = now.ToString("yyyy");
                var mm = now.ToString("MM");
                var dd = now.ToString("dd");

                var fileName = Guid.NewGuid().ToString("n") + ext;

                var folder = Path.Combine(_config.PhysicalPath, App, yy, mm, dd);
                if (!Directory.Exists(folder))
                {
                    Directory.CreateDirectory(folder);
                }

                var filePath = Path.Combine(folder, fileName);

                using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
                {
                    await file.CopyToAsync(fileStream);
                     fileStream.Flush(true);
                }
                var fileUrl = $"{_config.RootUrl}/{App}/{yy}/{mm}/{dd}/{fileName}";

                return new UploadResult() { url = fileUrl, ok = true };
            }
            catch (Exception ex)
            {
                LogUtil.Error(ex.Message);
                return new UploadResult() { msg = ex.Message };
            }
        }


        private (bool, string) ValidateExt(IFormFile file)
        {
            //后缀验证
            var ext = Path.GetExtension(file.FileName);
            if (!_config.AllowExts.Contains(ext, StringComparison.OrdinalIgnoreCase))
            {
                return (false, "不允许文件扩展名");

            }
            return (true, "ok");
        }

        /// <summary>
        /// 记录上传文件块的数量，用于合并前的检测
        /// </summary>
        private ConcurrentDictionary<string, int> countDict =
            new ConcurrentDictionary<string, int>();

        public LocalFileManager(IOptions<UploadServerConfig> config)
        {
            _config = config.Value;
        }

        private async Task<string> MergeChunkFiles(string app, string ext,
            string[] chunkFiles)
        {
            //上传逻辑
            var now = DateTime.Now;
            var yy = now.ToString("yyyy");
            var mm = now.ToString("MM");
            var dd = now.ToString("dd");

            var fileName = Guid.NewGuid().ToString("n") + ext;

            var folder = Path.Combine(_config.PhysicalPath, app, yy, mm, dd);
            if (!Directory.Exists(folder))
            {
                Directory.CreateDirectory(folder);
            }

            var filePath = Path.Combine(folder, fileName);
            using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
            {
                foreach (var chunkFile in chunkFiles.OrderBy(x => int.Parse(x.Substring(x.LastIndexOf(".") + 1))))
                {
                    using (var chunkStream = System.IO.File.OpenRead(chunkFile))
                    {
                        await chunkStream.CopyToAsync(fileStream);
                    }
                    //是否要删除块
                    System.IO.File.Delete(chunkFile);
                }
            }
            var fileUrl = $"{_config.RootUrl}/{app}/{yy}/{mm}/{dd}/{fileName}";
            return fileUrl;
        }
        string ValidateRequest(int chunkNumber, int chunkSize, long totalSize, string identifier, string filename,
                  long? fileSize, int totalChunks, long maxFileSize)
        {
            // 检查请求是否正确
            if (chunkNumber == 0 || chunkSize == 0 || totalSize == 0 || identifier.Length == 0 || filename.Length == 0)
            {
                return "非上传请求";
            }

            if (chunkNumber > totalChunks)
            {
                return "无效的上传请求1";
            }

            //是否文件太大
            if (totalSize > maxFileSize)
            {
                return "无效的上传请求2";
            }

            if (fileSize.HasValue)
            {
                if (chunkNumber < totalChunks && fileSize != chunkSize)
                {
                    //post请求中的块大小不正确
                    return "无效的上传请求3";
                }

                if (totalChunks > 1 && chunkNumber == totalChunks &&
                    fileSize != ((totalSize % chunkSize) + chunkSize))
                {
                    // The chunks in the POST is the last one, and the fil is not the correct size
                    return "无效的上传请求4";
                }

                if (totalChunks == 1 && fileSize != totalSize)
                {
                    //文件只是一个块，数据大小不适合
                    return "无效的上传请求5";
                }
            }

            return null;
        }
        void GetParams(HttpRequest request , out int chunkNumber, out int chunkSize, out long totalSize,
        out string identifier, out string filename, out string relativePath, out int totalChunks)
        {
           
            int.TryParse(request.Form["chunkNumber"], out  chunkNumber);
            int.TryParse(request.Form["chunkSize"], out chunkSize);
            int.TryParse(request.Form["chunkNumber"], out chunkNumber);
            long.TryParse(request.Form["totalSize"], out totalSize);
            int.TryParse(request.Form["totalChunks"], out totalChunks);
            string identifiers = request.Form["identifiers"];

            filename = request.Form["filename"];
            relativePath = request.Form["relativePath"];
            identifier = request.Form["identifier"];

            if (!string.IsNullOrWhiteSpace(identifier))
            {
                identifier = Regex.Replace(identifier, "[^0-9A-Za-z_-]", "", RegexOptions.Multiline);
            }
        }


        public string GetChunkFilename(int chunkNumber, string identifier)
        {
            return GetChunkFilename(_config.PhysicalPath, chunkNumber, identifier);
        }

        string GetChunkFilename(string physicalPath, int chunkNumber, string identifier)
        {
            // What would the file name be?
            var temp = Path.Combine(physicalPath, temporaryFolder);
            if (!Directory.Exists(temp))
            {
                Directory.CreateDirectory(temp);
            }

            return Path.Combine(physicalPath, temporaryFolder, "uploader-" + identifier + "." + chunkNumber);
        }

    }
}
